
import * as utc from "../util/utc.js";
import {newProduct} from "./productUtils.js";
import {tr} from "../ux/translations.js";
import * as sources from "./sources.js";
import {scalarProduct} from "./scalarProduct.js";
import {isKioskContext} from "../util/context.js";
import {ø} from "../util/objects.js";
import {resolveValidTime} from "./productUtils.js";
import {createUnitDescriptors} from "../util/units.js";
import {gaiaPath} from "./gaia.js";
import {createCO2Palette} from "../palette/co2.js";
import {createCO2SCVariantAPalette, createCO2SCVariantBPalette} from "../palette/co2sc.js";
import {createCOSCPalette} from "../palette/cosc.js";
import {createSO2SmassPalette} from "../palette/so2smass.js";
import {createDuexttauPalette} from "../palette/duexttau.js";
import {createSuexttauPalette} from "../palette/suexttau.js";


const aotLink = isKioskContext() ? "" : "(<a href='about.html#aot' class='internal-link'>AOT</a>)";

// This is the day when GAIA changed from processing GEOS-5 on 3-hourly steps to 1-hourly steps.
const TIMELINE_CHANGE_DATE = utc.date({year: 2019, month: 9, day: 1});

let palettes = {};

function geosPath(attr, type, alignment, offset) {
    // UNDONE: make layer.path a function and then use this.validTime() instead of computing it here
    const {time_cursor} = attr;
    if (alignment === undefined) {
        alignment = (time_cursor === "current" || utc.date(time_cursor) > TIMELINE_CHANGE_DATE) ? {hour: 1} : {hour: 3};
    }
    const validTime = resolveValidTime(time_cursor, alignment, offset);
    const dir = time_cursor === "current" ? "current" : utc.print(validTime, "{YYYY}/{MM}/{DD}");
    const stamp = time_cursor === "current" ? "current" : utc.print(validTime, "{hh}{mm}");
    const file = [stamp, type, "geos.epak"].join("-");
    return gaiaPath("data/geos", dir, file);
}

function nav1hr(attr, offset) {
    const validTime = resolveValidTime(attr.time_cursor, {hour: 1}, offset);
    const stepper = step => function() {
        return resolveValidTime(utc.add(this.validTime(), step), {hour: 1});
    };
    return {
        validTime: () => ø(validTime),
        prev2: stepper({hour: -8}),
        prev1: stepper({hour: -1}),
        next1: stepper({hour: +1}),
        next2: stepper({hour: +8}),
        navLabels: {
            prev2: `-8 ${tr`hours`}`,
            prev1: `-1 ${tr`hour`}`,
            next1: `+1 ${tr`hour`}`,
            next2: `+8 ${tr`hours`}`,
        },
    };
}

function nav3hr(attr, offset) {
    const validTime = resolveValidTime(attr.time_cursor, {hour: 3}, offset);
    const stepper = step => function() {
        return resolveValidTime(utc.add(this.validTime(), step), {hour: 3});
    };
    return {
        validTime: () => ø(validTime),
        prev2: stepper({day:  -1}),
        prev1: stepper({hour: -3}),
        next1: stepper({hour: +3}),
        next2: stepper({day:  +1}),
        navLabels: {
            prev2: `-1 ${tr`day`}`,
            prev1: `-3 ${tr`hours`}`,
            next1: `+3 ${tr`hours`}`,
            next2: `+1 ${tr`day`}`,
        },
    };
}

function newGEOSProduct(attr, offset) {
    // UNDONE: compute validTime here and pass into the nav1+nav3. also allows removal of offset.
    //         then co2sc can pass validTime directly into nav3
    const {time_cursor = "current"} = attr;
    const nav = (time_cursor === "current" || utc.date(time_cursor) > TIMELINE_CHANGE_DATE) ? nav1hr : nav3hr;
    return {
        sourceHTML: sources.geos,
        ...nav(attr, offset),
    };
}

export function createCO2Layer(attr) {
    return ø(newProduct(), newGEOSProduct(attr), {
        type: "co2",
        descriptionHTML: {
            name: tr`Carbon Dioxide Mixing Ratio`,
            qualifier: "",
        },
        paths: [geosPath(attr, "co2")],
        builder: file => scalarProduct(file, /CO2CL/, {hasMissing: false}),
        unitDescriptors: createUnitDescriptors({  // CO2 Bulk Mixing Ratio (Column Mass/ps), units: mol/mol
         // "ppmv":     {convert: x => x * 1e6, precision: 1},
            "µmol/mol": {convert: x => x * 1e6, precision: 1},
        }),
        alpha: {single: 160, animated: 160},
        scale: palettes.co2 ??= createCO2Palette(),
    });
}

export function createCOSCLayer(attr) {
    return ø(newProduct(), newGEOSProduct(attr), {
        type: "cosc",
        descriptionHTML: {
            name: tr`Carbon Monoxide Surface Concentration`,
            qualifier: "",
        },
        paths: [geosPath(attr, "cosc")],
        builder: file => scalarProduct(file, /COSC/, {hasMissing: false}),
        unitDescriptors: createUnitDescriptors({  // CO Surface Concentration in ppbv, units: 1e-9
            "ppbv": {convert: x => x,        precision: 0},
            "ppmv": {convert: x => x / 1000, precision: 2},
        }),
        alpha: {single: 160, animated: 140},
        scale: palettes.cosc ??= createCOSCPalette(),
    });
}

export function createSO2SmassLayer(attr) {
    return ø(newProduct(), newGEOSProduct(attr), {
        type: "so2smass",
        descriptionHTML: {
            name: tr`Sulfur Dioxide Surface Mass`,
            qualifier: "",
        },
        paths: [geosPath(attr, "so2smass")],
        builder: file => scalarProduct(file, /SO2SMASS/, {hasMissing: false}),
        unitDescriptors: createUnitDescriptors({  // SO2 Surface Mass Concentration, units: kg/m^3
            "µg/m^3": {convert: x => x * 1e9,                precision: 2},
         // "ppb":    {convert: x => x * 1e9 / 2.86,         precision: 0},
         // "ppm":    {convert: x => x * 1000 * 1000 / 2.86, precision: 3},
         // "mg/m^3": {convert: x => x * 1000 * 1000,        precision: 3},
         // "kg/m^3": {convert: x => x,                      precision: 14},
        }),
        alpha: {single: 160, animated: 140},
        scale: palettes.so2smass ??= createSO2SmassPalette(),
    });
}

export function createDuexttauLayer(attr) {
    return ø(newProduct(), newGEOSProduct(attr), {
        type: "duexttau",
        descriptionHTML: {
            name: `${tr`Dust Extinction (Aerosol Optical Thickness, 550 nm)`} ${aotLink}`,
            qualifier: "",
        },
        paths: [geosPath(attr, "duexttau")],
        builder: file => scalarProduct(file, /DUEXTTAU/, {hasMissing: false}),
        unitDescriptors: createUnitDescriptors({  // Dust Extinction AOT (aerosol optical thickness) [550 nm], units: τ
            "τ": {convert: x => x, precision: 4},
        }),
        alpha: {single: 160, animated: 140},
        scale: palettes.duexttau ??= createDuexttauPalette(),
    });
}

export function createSuexttauLayer(attr) {
    return ø(newProduct(), newGEOSProduct(attr), {
        type: "suexttau",
        descriptionHTML: {
            name: `${tr`Sulfate Extinction (Aerosol Optical Thickness, 550 nm)`} ${aotLink}`,
            qualifier: "",
        },
        paths: [geosPath(attr, "suexttau")],
        builder: file => scalarProduct(file, /SUEXTTAU/, {hasMissing: false}),
        unitDescriptors: createUnitDescriptors({  // SO4 Extinction AOT (aerosol optical thickness) [550 nm], units: τ
            "τ": {convert: x => x, precision: 3},
        }),
        alpha: {single: 240, animated: 140},
        scale: palettes.suexttau ??= createSuexttauPalette(),
    });
}

export function createCO2scLayer(attr) {
    const alignment = {hour: 3};
    const offset = {minute: 90};
    const validTime = resolveValidTime(attr.time_cursor, alignment, offset);
    const cutoff = utc.date({year: 2017, month: 1, day: 24, hour: 4, minute: 30});
    const adjustmentRequired = utc.date(validTime) < cutoff;  // old version of GEOS-5 needs CO2 offset
    const Δ = adjustmentRequired ? 32 : 0;
    const upgradeDate = utc.date({year: 2018, month: 7, day: 11, hour: 4, minute: 30});
    const variant = utc.date(validTime) < upgradeDate ? "A" : "B";

    function applyDelta(data) {
        for (let i = 0; i < data.length; i++) {
            data[i] += Δ;
        }
    }

    const palette =
        variant === "A" ?
            adjustmentRequired ?
                palettes.co2scAX ??= createCO2SCVariantAPalette(-8) :
                palettes.co2scAY ??= createCO2SCVariantAPalette(0) :
            palettes.co2scB ??= createCO2SCVariantBPalette();

    return ø(newProduct(), newGEOSProduct(attr), {
        type: "co2sc",
        descriptionHTML: {
            name: tr`Carbon Dioxide Surface Concentration`,
            qualifier: "",
        },
        sourceHTML: adjustmentRequired ? sources.geosAdjustedCO2 : sources.geos,
        paths: [geosPath(attr, "co2sc", alignment, offset)],
        ...nav3hr(attr, offset),
        builder: file => scalarProduct(file, /CO2SC/, {hasMissing: false, transform: Δ !== 0 ? applyDelta : a => a}),
        unitDescriptors: createUnitDescriptors({  // CO2 Surface Concentration, units: ppmv
            "ppmv": {convert: x => x, precision: 0},
        }),
        alpha: {single: 200, animated: 150},
        scale: palette,
    });
}
